package com.db.williamchart.renderer;

import com.db.williamchart.ChartContract;
import com.db.williamchart.animation.ChartAnimation;
import com.db.williamchart.data.DonutDataPoint;
import com.db.williamchart.data.Frame;
import com.db.williamchart.data.configuration.DonutChartConfiguration;
import com.db.williamchart.extensions.ExtensionUtils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class DonutChartRenderer implements ChartContract.DonutRenderer{

    private final int fullDegrees = 360;
    private final float ignoreStartPosition = -1234f;

    private ChartContract.DonutView view;
    private ChartAnimation<DonutDataPoint> animation;

    private Frame innerFrameWithStroke;
    private List<DonutDataPoint> datapoints;
    private DonutChartConfiguration chartConfiguration;

    public DonutChartRenderer(ChartContract.DonutView view, ChartAnimation<DonutDataPoint> animation) {
        this.view = view;
        this.animation = animation;
        this.innerFrameWithStroke = new Frame(0f, 0f, 0f, 0f);
        this.datapoints = new ArrayList<>();
    }


    @Override
    public boolean preDraw(DonutChartConfiguration configuration) throws IllegalAccessException {

        chartConfiguration = configuration;

        if (chartConfiguration.getColorsSize() < datapoints.size()) {
            throw new IllegalAccessException(
                    "Number of datapoints is " + datapoints.size() +
                            "but only " + chartConfiguration.getColorsSize() + " color(s) provided."
            );
        }

        float left =
                configuration.getPaddings().getLeft() + configuration.getThickness() / 2;
        float top =
                configuration.getPaddings().getTop() + configuration.getThickness() / 2;
        float right =
                configuration.getWidth() - configuration.getPaddings().getRight() - configuration.getThickness() / 2;
        float bottom =
                configuration.getHeight() - configuration.getPaddings().getBottom() - configuration.getThickness() / 2;

        innerFrameWithStroke = new Frame(left, top, right, bottom);

        for (DonutDataPoint point : datapoints) {
            point.setScreenDegrees(
                    point.getValue() * fullDegrees / chartConfiguration.getTotal()
            );
        }
        Collections.sort(datapoints, new Comparator<DonutDataPoint>() {
            @Override
            public int compare(DonutDataPoint o1, DonutDataPoint o2) {
                if (o1.getScreenDegrees() > o2.getScreenDegrees()) {
                    return -1;
                } else if (o1.getScreenDegrees() < o2.getScreenDegrees()) {
                    return 1;
                } else {
                    return 0;
                }
            }
        });

        animation.animateFrom(ignoreStartPosition, datapoints, new ChartAnimation.AnimateFromCallback() {
            @Override
            public void callback() {
                view.postInvalidate();
            }
        });

        return true;
    }

    @Override
    public void draw() {
        if (chartConfiguration.getBarBackgroundColor() != 0) {
            view.drawBackground(innerFrameWithStroke);
        }

        List<Float> floats = new ArrayList<>();
        for (DonutDataPoint point : datapoints) {
            floats.add(point.getScreenDegrees());
        }
        view.drawArc(floats, innerFrameWithStroke);
    }

    @Override
    public void render(List<Float> values) {
        List<Float> valuesOffset = generateValuesOffset(values);
        datapoints = new ArrayList<>();
        for (int i = 0; i < values.size(); i++) {
            DonutDataPoint point = ExtensionUtils.toDonutDataPoint(values.get(i), valuesOffset.get(i));
            datapoints.add(point);
        }
        view.postInvalidate();
    }

    @Override
    public void anim(List<Float> values, ChartAnimation<DonutDataPoint> animation) {
        List<Float> valuesOffset = generateValuesOffset(values);
        datapoints = new ArrayList<>();
        for (int i = 0; i < values.size(); i++) {
            DonutDataPoint point = ExtensionUtils.toDonutDataPoint(values.get(i), valuesOffset.get(i));
            datapoints.add(point);
        }
        this.animation = animation;
        view.postInvalidate();
    }

    private List<Float> generateValuesOffset(List<Float> list) {
        List<Float> valuesOffset = new ArrayList<>();
        for (int i = 0; i < list.size(); i++) {
            float offset = 0f;
            if (i > 0) {
                offset = valuesOffset.get(i - 1) + list.get(i - 1);
            }
            valuesOffset.add(offset);
        }
        return valuesOffset;
    }
}
